package com.yygo.dao.hibernate;

import com.yygo.Constants;
import com.yygo.dao.MaterialAuditDao;
import com.yygo.model.*;
import com.yygo.model.enums.MaterialStatus;
import com.yygo.model.enums.RequestStatus;
import com.yygo.util.StringUtil;
import org.displaytag.properties.SortOrderEnum;
import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.IdentifierLoadAccess;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by tiansha on 2015/11/20.
 */
@Repository("materialAuditDao")
public class MaterialAuditDaoHibernate extends GenericDaoHibernate<MaterialAudit, Long> implements MaterialAuditDao {

    public MaterialAuditDaoHibernate() {
        super(MaterialAudit.class);
    }

    @Override
    public int getSendLoanRequestCount(User user, String status, Date date, String number, String name, String shipNumber) {
        Criteria criteria = buildSendCriteria(user, status, date, number, name, shipNumber);
        criteria.createCriteria("materialAudit").add(Restrictions.eq("allSent", false));
        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        criteria.setProjection(Projections.rowCount());
        return Integer.parseInt(criteria.uniqueResult().toString());
    }

    @Override
    public List<LoanRequest> searchSendLoanRequests(User user, String status, Date date, String number, String name, String shipNumber, int firstResult, int maxResults, SortOrderEnum sortDirection, String sortCriterion) {
        Criteria criteria = buildSendCriteria(user, status, date, number, name, shipNumber);
        criteria.add(Restrictions.ne("status", RequestStatus.credit_denied));
        criteria.add(Restrictions.ne("status", RequestStatus.loan_denied));
        criteria.add(Restrictions.ne("status", RequestStatus.request_denied));
        criteria.createCriteria("materialAudit").add(Restrictions.eq("allSent", false));
        setPagination(criteria, firstResult, maxResults, sortDirection, sortCriterion);

        criteria.setProjection(Projections.distinct(Projections.property("id")));
        //criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

        List<Long> ids = criteria.list();
        List<LoanRequest> requests = new ArrayList<>();
        for (Long id : ids) {
            requests.add(getLoanRequest(id));
        }
        return requests;
    }

    @Override
    public MaterialAudit get( Long id){
        MaterialAudit materialAudit = getHibernateTemplate().get(MaterialAudit.class, id);
        Hibernate.initialize(materialAudit.getMaterialList());
        return materialAudit;
    }

    @Override
    public int getReceiveLoanRequestCount(User user, String shipNumber) {
        Criteria criteria = buildReceiveCriteria(user, shipNumber);
        criteria.add(Restrictions.or(Restrictions.ne("allReceived", true), Restrictions.and(Restrictions.eq("materialStatus", MaterialStatus.Receiving), Restrictions.eq("allReceived", true))));
        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        criteria.setProjection(Projections.rowCount());
        return Integer.parseInt(criteria.uniqueResult().toString());
    }

    @Override
    public List<LoanRequest> searchReceiveLoanRequests(User user, String shipNumber, int firstResult, int maxResults, SortOrderEnum sortDirection, String sortCriterion) {
        Criteria criteria = buildReceiveCriteria(user, shipNumber);
        setPagination(criteria, firstResult, maxResults, sortDirection, sortCriterion);
        criteria.add(Restrictions.or(Restrictions.ne("allReceived", true),Restrictions.and(Restrictions.eq("materialStatus", MaterialStatus.Receiving),Restrictions.eq("allReceived", true))));
        criteria.setProjection(Projections.distinct(Projections.property("id")));
        //criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

        List<Long> ids = criteria.list();
        List<Long> loanRequestIds = new ArrayList<>();
        List<LoanRequest> requests = new ArrayList<>();
        for (Long id : ids) {
            LoanRequest lr = get(id).getLoanRequest();
            if(lr!=null && !loanRequestIds.contains(lr.getId())) {
                requests.add(lr);
                loanRequestIds.add(lr.getId());
            }
        }
        return requests;
    }

    @Override
    public int getAuditLoanRequestCount(User user, String number) {
        Criteria criteria = buildAuditCriteria(user, number);
        criteria.add(Restrictions.eq("materialStatus" ,MaterialStatus.Auditing));
        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        criteria.setProjection(Projections.rowCount());
        return Integer.parseInt(criteria.uniqueResult().toString());
    }

    @Override
    public List<LoanRequest> searchAuditLoanRequests(User user, String number, int firstResult, int maxResults, SortOrderEnum sortDirection, String sortCriterion) {
        Criteria criteria = buildAuditCriteria(user, number);
        criteria.add(Restrictions.eq("materialStatus" ,MaterialStatus.Auditing));
        setPagination(criteria, firstResult, maxResults, sortDirection, sortCriterion);

        criteria.setProjection(Projections.distinct(Projections.property("id")));
        //criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

        List<Long> ids = criteria.list();
        List<Long> loanRequestIds = new ArrayList<>();
        List<LoanRequest> requests = new ArrayList<>();
        for (Long id : ids) {
            LoanRequest lr = get(id).getLoanRequest();
            if(lr!=null && !loanRequestIds.contains(lr.getId())) {
                requests.add(lr);
                loanRequestIds.add(lr.getId());
            }
        }
        return requests;
    }

    @Override
    public int getPrintLoanRequestCount(User user, String number) {
        Criteria criteria = buildPrintCriteria(user, number);
        criteria.add(Restrictions.eq("materialStatus" ,MaterialStatus.Printing));
        criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        criteria.setProjection(Projections.rowCount());
        return Integer.parseInt(criteria.uniqueResult().toString());
    }

    @Override
    public List<LoanRequest> searchPrintLoanRequests(User user, String number, int firstResult, int maxResults, SortOrderEnum sortDirection, String sortCriterion) {
        Criteria criteria = buildPrintCriteria(user, number);
        criteria.add(Restrictions.eq("materialStatus" ,MaterialStatus.Printing));
        setPagination(criteria, firstResult, maxResults, sortDirection, sortCriterion);

        criteria.setProjection(Projections.distinct(Projections.property("id")));
        //criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

        List<Long> ids = criteria.list();
        List<Long> loanRequestIds = new ArrayList<>();
        List<LoanRequest> requests = new ArrayList<>();
        for (Long id : ids) {
            LoanRequest lr = get(id).getLoanRequest();
            if(lr!=null && !loanRequestIds.contains(lr.getId())) {
                requests.add(lr);
                loanRequestIds.add(lr.getId());
            }
        }
        return requests;
    }

    private Criteria buildSendCriteria(User user, String status, Date date, String number, String name, String shipNumber) {
        Criteria criteria = getSession().createCriteria(LoanRequest.class).add(Restrictions.ge("requestDate", date));
        boolean isDealer = isDealer(user);
        if (!status.equals("all")){
            RequestStatus requestStatus = RequestStatus.valueOf(status);
            criteria.add(Restrictions.eq("status", requestStatus));
        }

        if (!StringUtil.isBlankOrNull(number)) {
            criteria.add(Restrictions.eq("number", number));
        }
        if (!StringUtil.isBlankOrNull(name)) {
            criteria.createAlias("requestPerson", "requestPerson").add(Restrictions.eq("requestPerson.name", name));
        }

        if (!StringUtil.isBlankOrNull(shipNumber)) {
            criteria.createCriteria("materialList").add(Restrictions.eq("shipNumber", shipNumber));
        }

        if (isDealer) {
            criteria.add(Restrictions.eq("dealerUser", user));
        }

        criteria.addOrder(Order.desc("requestDate"));
        return criteria;
    }

    private Criteria buildReceiveCriteria(User user, String shipNumber) {
        Criteria criteria = getSession().createCriteria(MaterialAudit.class);

        if (!StringUtil.isBlankOrNull(shipNumber)) {
            criteria.createCriteria("materialList").add(Restrictions.eq("shipNumber", shipNumber));
        }
        criteria.createCriteria("loanRequest").addOrder(Order.desc("requestDate")).createCriteria("loanProduct").add(Restrictions.eq("bank", user.getBank()));

        //criteria.createCriteria("loanRequest").addOrder(Order.desc("requestDate"));
        return criteria;
    }

    private Criteria buildAuditCriteria(User user, String number) {
        Criteria criteria = getSession().createCriteria(MaterialAudit.class);

        if (!StringUtil.isBlankOrNull(number)) {
            criteria.createCriteria("loanRequest").add(Restrictions.eq("number", number)).addOrder(Order.desc("requestDate")).createCriteria("loanProduct").add(Restrictions.eq("bank", user.getBank()));
        }
        else criteria.createCriteria("loanRequest").addOrder(Order.desc("requestDate")).createCriteria("loanProduct").add(Restrictions.eq("bank", user.getBank()));
        return criteria;
    }

    private Criteria buildPrintCriteria(User user, String number) {
        Criteria criteria = getSession().createCriteria(MaterialAudit.class);

        if (!StringUtil.isBlankOrNull(number)) {
            criteria.createCriteria("loanRequest").add(Restrictions.eq("number", number)).add(Restrictions.eq("number", number)).addOrder(Order.desc("requestDate")).createCriteria("loanProduct").add(Restrictions.eq("bank", user.getBank()));
        }
        else criteria.createCriteria("loanRequest").addOrder(Order.desc("requestDate")).createCriteria("loanProduct").add(Restrictions.eq("bank", user.getBank()));
        return criteria;
    }

    private void setPagination(Criteria criteria, int firstResult, int maxResults, SortOrderEnum sortDirection, String sortCriterion) {
        criteria.setFirstResult(firstResult);
        criteria.setFetchSize(maxResults);
        if (sortCriterion != null) {
            if (sortDirection.equals(SortOrderEnum.ASCENDING)) {
                criteria.addOrder(Order.asc(sortCriterion));
            }
            if (sortDirection.equals(SortOrderEnum.DESCENDING)) {
                criteria.addOrder(Order.desc(sortCriterion));
            }
        }
    }

    private LoanRequest getLoanRequest(Long id) {
        IdentifierLoadAccess byId = getSession().byId(LoanRequest.class);
        return (LoanRequest) byId.load(id);
    }

    private boolean isDealer(User user) {
        return user != null && (user.hasRole(Constants.DEALER_USER_ROLE));
    }
}
